#ifndef __FIXEDMEMMGR_H__
#define __FIXEDMEMMGR_H__

class CFixedMemMgr : 
    public CBaseUnk<FixedSizeMemoryManager>
{
public:
    UNKNOWN_IMPL
    // Allocator fills in constant default values.  Other
    // values set in CreateInstance.
    CFixedMemMgr() : 
        m_pFirstBlock(NULL), 
        m_pFree(NULL),
        m_cFreshItems(0){}
    ~CFixedMemMgr();
    static HRESULT CreateInstance(const long ElementSize, const long ElementsPerBlock, FixedSizeMemoryManager** ppMemMgr);
    STDMETHOD(Alloc)( 
        /* [retval][out] */ long __RPC_FAR *retVal);
    STDMETHOD_(void, Free)( 
        /* [in] */ long pMemory);
    STDMETHOD(get_ElementSize)( 
        /* [retval][out] */ long __RPC_FAR *retVal);
    STDMETHOD(get_ElementsPerBlock)( 
        /* [retval][out] */ long __RPC_FAR *retVal);
private:
    bool AllocBlock();
    
    // A linked list of blocks. The first 4 bytes of each
    // block are used to chain them together. Singly linked
    // with new items added to the head.
    void* m_pFirstBlock;
    // A list of items that have been allocated then freed
    void* m_pFree;
    // The next available position in the current m_pFirstBlock.
    // These values have never been in the free list. Tracking both
    // the latest block and the free list takes a few extra bytes here
    // and an extra check in Alloc, but eliminates the need to walk all
    // items in a new block to populate the free list.
    void* m_pFreshItem;
    // The number of fresh items in the list
    long m_cFreshItems;
    // The aligned element size
    long m_ElemSize;
    // The number of elements to allocate from the system
    // at a single shot.
    long m_ElemsPerBlock;
};

class CFixedMemMgrCompact : 
    public CBaseUnk<CompactibleFixedSizeMemoryManager>
{
public:
    STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj)
    {
        return _BaseQI(FastIsEqualIID(riid, __uuidof(FixedSizeMemoryManager)) ?
                         __uuidof(CompactibleFixedSizeMemoryManager) : riid, ppvObj);
    }
    STDMETHOD_(ULONG, AddRef)(){return _BaseAddRef();}
    STDMETHOD_(ULONG, Release)(){return _BaseRelease();}
    // Allocator fills in constant default values.  Other
    // values set in CreateInstance.
    CFixedMemMgrCompact() : 
        m_pFirstBlock(NULL), 
        m_fCompactOnFree(VARIANT_FALSE), 
        m_cEmptyBlocks(0), 
        m_cBufferBlocks(1){}
    ~CFixedMemMgrCompact();
    static HRESULT CreateInstance(const long ElementSize, const long ElementsPerBlock, FixedSizeMemoryManager** ppMemMgr);
    STDMETHOD(Alloc)( 
        /* [retval][out] */ long __RPC_FAR *retVal);
    STDMETHOD_(void, Free)( 
        /* [in] */ long pMemory);
    STDMETHOD(get_ElementSize)( 
        /* [retval][out] */ long __RPC_FAR *retVal);
    STDMETHOD(get_ElementsPerBlock)( 
        /* [retval][out] */ long __RPC_FAR *retVal);
    STDMETHOD_(void, Compact)( void);
    STDMETHOD(get_CompactOnFree)( 
        /* [retval][out] */ VARIANT_BOOL __RPC_FAR *retVal);
    STDMETHOD(put_CompactOnFree)( 
        /* [in] */ VARIANT_BOOL RHS);
    STDMETHOD(get_BufferBlocks)( 
        /* [retval][out] */ short __RPC_FAR *retVal);
    STDMETHOD(put_BufferBlocks)( 
        /* [in] */ short RHS);
private:
    struct CBlock
    {
        CBlock* pNext;
        void* pFree;
        long cInUse;
        bool fFreeIsList;
    };
    friend struct CBlock;
    
    CBlock* AllocBlock();
    
    // The first block structure
    CBlock* m_pFirstBlock;
    // The aligned element size
    long m_ElemSize;
    // The size of a block allocateion
    long m_BlockSize;
    // The number of elements to allocate from the system
    // at a single shot.
    long m_ElemsPerBlock;
    // The number of empty blocks
    short m_cEmptyBlocks;
    // The number of blocks to keep as a buffer
    short m_cBufferBlocks;
    // Whether or not to compact blocks on every Free call
    VARIANT_BOOL m_fCompactOnFree;
};

#endif //__FIXEDMEMMGR_H__